package com.changpei.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.changpei.common.cache.ContentCache;
import com.changpei.common.cache.UserCache;
import com.changpei.common.result.Result;
import com.changpei.common.utils.FilePrefix;
import com.changpei.common.utils.IdsUtil;
import com.changpei.common.utils.JsonUtil;
import com.changpei.common.utils.PageUtil;
import com.changpei.core.feign.ContentTmpFeign;
import com.changpei.core.pojo.ContentTmp;
import com.changpei.core.pojo.User;
import com.changpei.search.config.RecommendConfig;
import com.changpei.search.dao.HomeIndexMapper;
import com.changpei.search.pojo.HomeIndex;
import com.changpei.search.service.ContentIndexService;
import com.changpei.search.service.HomeIndexService;
import com.changpei.search.utils.WeightUtil;
import com.changpei.search.vo.HomeIndexVO;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.GetQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 *
 *
 * @author Way on 2021/9/22
 */
@Service
@Slf4j
public class HomeIndexServiceImpl implements HomeIndexService {

    @Autowired
    private ContentTmpFeign contentTmpFeign;

    @Autowired
    private HomeIndexMapper homeIndexMapper;

    @Autowired
    private ContentIndexService contentIndexService;

    @Autowired
    private UserCache userCache;

    @Autowired
    private ElasticsearchTemplate esTemplate;

    @Autowired
    private RecommendConfig config;

    @Autowired
    private ContentCache contentCache;


    @Override
    public void importData() {
        List<ContentTmp> contents = contentTmpFeign.findHome().getData();

        String isValid = "http";

        List<HomeIndex> homeIndices = new ArrayList<>();

        for (ContentTmp tmp : contents) {
            List<JSONObject> contentFiles = contentIndexService.getContentFiles(tmp.getId());
            if (ObjectUtils.isEmpty(contentFiles)) {
                continue;
            }

            HomeIndex homeIndex = JsonUtil.jsonToBean(tmp, HomeIndex.class);
            User userInfo = contentIndexService.getUserInfo(tmp.getUserId());

            homeIndex.setFiles(contentFiles);
            if (!ObjectUtils.isEmpty(contentFiles)) {
                // 设置第一张图片为封面
                homeIndex.setCoveredImg(contentFiles.get(0));
            }
            homeIndex.setTags(contentIndexService.getContentTags(tmp.getId()));
            homeIndex.setNickName(userInfo.getNickName());
            if (!userInfo.getAvatarUrl().contains(isValid)) {
                homeIndex.setAvatarUrl(FilePrefix.AVATAR_URL_PREFIX + userInfo.getAvatarUrl());
            } else {
                homeIndex.setAvatarUrl(userInfo.getAvatarUrl());
            }
//            contentCache.setContentIds(homeIndex.getId());
            homeIndices.add(homeIndex);
        }

        homeIndexMapper.saveAll(homeIndices);
    }

    @Override
    public List<HomeIndexVO> home(Map<String, Object> map) {
        // 参数
        Long userId = userCache.getUserId(map.get("token").toString());
        int pageNum = Integer.parseInt(map.get("pageNum").toString());
        int pageSize = Integer.parseInt(map.get("pageSize").toString());
        String categoryId = map.get("categoryId").toString();

        List<HomeIndexVO> vos = new ArrayList<>();
        List<JSONObject> cacheUserData = userCache.getCacheUserData(userId, categoryId);
        if (cacheUserData.isEmpty()) {
            vos.addAll(userData(userId, categoryId, pageNum, pageSize));
        } else {
            // 获取缓存中的数据
            List<JSONObject> objects = PageUtil.startPage(cacheUserData, pageNum, pageSize);

            List<HomeIndexVO> voList = objects
                    .stream()
                    .map(o -> JsonUtil.jsonToBean(o, HomeIndexVO.class))
                    .collect(Collectors.toList());
            vos.addAll(voList);

            // 移除缓存中的数据
            cacheUserData.removeAll(objects);
            userCache.cacheUserData(userId, categoryId, cacheUserData);
        }

        // 锁定到数据周期
        vos.forEach(o -> userCache.lockTmpUserData(userId, o.getId()));

        return vos;
    }

    private List<HomeIndexVO> userData(Long userId, String categoryId, int pageNum, int pageSize) {
        Set<Long> set = contentIndexService.myLockData(userId);

        // 用户标签
        LinkedList<String> tags = userCache.getTags(userId);

        // 我的关注
        Set<Long> followers = userCache.followers(userId);

        NativeSearchQueryBuilder nb = new NativeSearchQueryBuilder();

        BoolQueryBuilder boolQuery = new BoolQueryBuilder();
        // 匹配分类查询
        String isRecommendKey = "1";
        if (!isRecommendKey.equals(categoryId)) {
            boolQuery.must(QueryBuilders.termQuery("categoryId", categoryId));
        }

        if (!set.isEmpty()) {
            boolQuery.mustNot(QueryBuilders.termsQuery("id", set));
        }

        // 匹配用户标签
        if (!tags.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            tags.forEach(sb::append);
            boolQuery.should(QueryBuilders.matchQuery("tags", sb.toString()));
        }

        // 匹配我的关注
        if (!followers.isEmpty()) {
            // 我的关注权重
            Integer weight = WeightUtil.countWithWeight(config.getFollow(), config.getCount());

            Set<Long> userIds = new HashSet<>();
            if (followers.size() < weight) {
                userIds.addAll(IdsUtil.getIds(followers.size(), Long.parseLong(String.valueOf(followers.size()))));
            } else {
                userIds.addAll(IdsUtil.getIds(weight, Long.parseLong(String.valueOf(followers.size()))));
            }
            boolQuery.should(QueryBuilders.termsQuery("userId", userIds));
        }

        nb.withQuery(boolQuery);

        // 默认缓存一千条
        nb.withPageable(PageRequest.of(0, 1000));

        List<HomeIndexVO> voList = esTemplate.queryForPage(nb.build(), HomeIndexVO.class).getContent();

        if (!voList.isEmpty()) {
            // 匹配我的关注
            List<HomeIndexVO> vos = peekFollowed(userId, voList);

            // 转型
            List<JSONObject> objects = vos
                    .stream()
                    .map(o -> JSONObject.parseObject(JSON.toJSONString(o)))
                    .collect(Collectors.toList());

            userCache.cacheUserData(userId, categoryId, objects);

            return PageUtil.startPage(vos, pageNum, pageSize);
        }

        return new ArrayList<>();
    }

    @Override
    public HomeIndex findById(Long id) {
        GetQuery getQuery = new GetQuery();
        getQuery.setId(id.toString());
        HomeIndex homeIndex = esTemplate.queryForObject(getQuery, HomeIndex.class);
        return ObjectUtils.isEmpty(homeIndex) ? null : homeIndex;
    }

    /**
     * 匹配我的关注
     * @param userId
     * @param source
     * @return
     */
    private List<HomeIndexVO> peekFollowed(Long userId, List<HomeIndexVO> source) {
        Set<Long> followers = userCache.followers(userId);

        return source.stream().peek(o -> {
            o.setFollowed(false);
            followers.forEach(followerId -> {
                if (o.getUserId().equals(followerId)) {
                    o.setFollowed(true);
                }
            });
        }).collect(Collectors.toList());
    }

    @Override
    public Result<Boolean> update(HomeIndex homeIndex) {
        try {
            homeIndexMapper.save(homeIndex);
            return Result.ok();
        } catch (Exception e) {
            log.warn("Content数据更新ES库失败", e.getMessage());
            return Result.failed();
        }
    }
}
